package cn.org.rookie.jeesdp.service.impl;

import cn.org.rookie.jeesdp.core.BaseEntity;
import cn.org.rookie.jeesdp.core.Page;
import cn.org.rookie.jeesdp.core.utils.IdUtils;
import cn.org.rookie.jeesdp.core.utils.SecurityUtils;
import cn.org.rookie.jeesdp.mapper.BaseMapper;
import cn.org.rookie.jeesdp.mapper.annotation.PrimaryKey;
import cn.org.rookie.jeesdp.mapper.sql.Wrapper;
import cn.org.rookie.jeesdp.mapper.utils.StringUtils;
import cn.org.rookie.jeesdp.service.BaseService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

public abstract class BaseServiceImpl<E extends BaseEntity, P, M extends BaseMapper<E, P>> implements BaseService<E, P, M>, ApplicationContextAware, InitializingBean {

    protected Class<E> entityClass;
    protected Class<M> mapperClass;
    protected M mapper;
    protected ApplicationContext applicationContext;
    protected SqlSessionFactory sqlSessionFactory;

    @Override
    public String insert(E entity) {
        String id = IdUtils.snowflakeId();
        entity.setId(id);
        entity.setCreator(Objects.requireNonNull(SecurityUtils.getUser()).getName());
        entity.setCreateTime(new Date());
        mapper.insert(entity);
        return id;
    }

    @Override
    public List<String> insert(List<E> list) {
        SqlSession session = sqlSessionFactory.openSession(false);
        BaseMapper<E, P> mapper = session.getMapper(mapperClass);
        List<String> ids = new ArrayList<>();
        for (E entity : list) {
            String id = IdUtils.snowflakeId();
            entity.setId(id);
            entity.setCreator(Objects.requireNonNull(SecurityUtils.getUser()).getName());
            entity.setCreateTime(new Date());
            mapper.insert(entity);
            ids.add(id);
        }
        session.commit();
        return ids;
    }

    @Override
    public int delete(P param) {
        return mapper.delete(param);
    }

    @Override
    public int delete(List<P> params) {
        List<Field> fields = Arrays.asList(entityClass.getSuperclass().getDeclaredFields());
        fields.addAll(1, Arrays.asList(entityClass.getDeclaredFields()));
        String primaryKey = "";
        for (Field field : fields) {
            PrimaryKey anno = field.getAnnotation(PrimaryKey.class);
            if (anno != null) {
                primaryKey = StringUtils.humpToUnderline(field.getName());
            }
        }
        Wrapper wrapper = Wrapper.init().in(primaryKey, params.toArray());
        return mapper.deleteBy(wrapper);
    }

    @Override
    public int deleteBy(Wrapper wrapper) {
        return mapper.deleteBy(wrapper);
    }

    @Override
    public int update(E entity) {
        entity.setModifier(Objects.requireNonNull(SecurityUtils.getUser()).getName());
        entity.setModifyTime(new Date());
        return mapper.update(entity);
    }

    @Override
    public int updateBy(E entity, Wrapper wrapper) {
        entity.setModifier(Objects.requireNonNull(SecurityUtils.getUser()).getName());
        entity.setModifyTime(new Date());
        return mapper.updateBy(entity, wrapper);
    }

    @Override
    public String save(E entity) {
        if (entity.getId() == null) {
            return insert(entity);
        } else {
            int update = update(entity);
            return entity.getId();
        }
    }

    @Override
    public E find(P param) {
        return mapper.find(param);
    }

    @Override
    public List<E> findBy(Wrapper wrapper) {
        return mapper.findBy(wrapper);
    }

    @Override
    public List<E> findAll() {
        return mapper.findAll();
    }

    @Override
    public PageInfo<E> page(Page<E> page) {
        PageHelper.startPage(page.getPageNum(), page.getPageSize());
        String orderBy = page.getOrderBy();
        if (orderBy != null) {
            PageHelper.orderBy(page.getOrderBy());
        }
        List<E> result;
        E param = page.getParam();
        if (param != null) {
            result = findBy(Wrapper.init(param));
        } else {
            result = findAll();
        }
        return new PageInfo<>(result);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() {
        Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
        entityClass = (Class<E>) types[0];
        mapper = applicationContext.getBean((Class<? extends M>) types[2]);

    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
